<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title></title>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
    <style>
        .item {
            border: 1px solid #1da921;
            width: 180px;
            border-radius: 5px;
            box-shadow: 0 0 5px 0 #b3b3b3;
            margin: 5px auto;
            background: #fff;
        }

        .item.active {
            border-style: dashed;
        }

        .item-header {
            font-size: 12px;
            color: #9e9e9e;
            padding: 3px 5px;
        }

        .item-main {
            padding: 5px;
            font-size: 14px;
            color: #424242;
            height: 36px;
            overflow: hidden;
            text-overflow: ellipsis;
            display: -webkit-box;
            -webkit-box-orient: vertical;
            -webkit-line-clamp: 2;
        }

        .item-header-point {
            background: #ccc;
            float: right;
            padding: 0 4px;
            min-width: 10px;
            text-align: center;
            color: #fff;
            border-radius: 50%;
        }

        .col {
            border: 1px solid #d2d2d2;
            flex-grow: 1;
            width: 180px;
            height: 100%;
            margin: 0 2px;
            background: #eee;
            flex-grow: 1;
            display: flex;
            flex-direction: column;
        }

        .col-header {
            height: 40px;
            line-height: 40px;
            background: #1DA921;
            color: #fff;
            text-align: center;
        }

        .col-main {
            overflow: auto;
            flex-grow: 1;
        }

        .col-main.active {
            background: #00ad23;
            opacity: 0.1;
        }

        .task-wrapper {
            display: flex;
            height: 400px;
            width: 700px;
        }
    </style>
</head>

<body>

    <div id="app"></div>

    <script type="text/babel">
        const STATUS_TODO = 'STATUS_TODO';
        const STATUS_DOING = 'STATUS_DOING';
        const STATUS_DONE = 'STATUS_DONE';

        const STATUS_CODE = {
            STATUS_TODO: '待处理',
            STATUS_DOING: '进行中',
            STATUS_DONE: '已完成'
        }

        // 这个数据结构定义的秒呀，point从大到小排序，保证了你不管怎么拖拽到哪个状态，都是优先级高的在前面
        let tasks = [{
            id: 0,
            status: STATUS_TODO,
            title: '每周七天阅读五次，每次阅读完要做100字的读书笔记',
            username: '小夏',
            point: 10
        }, {
            id: 1,
            status: STATUS_TODO,
            title: '每周七天健身4次，每次健身时间需要大于20分钟',
            username: '橘子?',
            point: 5
        }, {
            id: 2,
            status: STATUS_TODO,
            title: '单词*100',
            username: '┑(￣Д ￣)┍',
            point: 2
        }, {
            id: 3,
            status: STATUS_TODO,
            title: '单词*150',
            username: '┑(￣Д ￣)┍',
            point: 2
        }, {
            id: 4,
            status: STATUS_TODO,
            title: '单词*200',
            username: '┑(￣Д ￣)┍',
            point: 2
        }, {
            id: 5,
            status: STATUS_TODO,
            title: '单词*250',
            username: '┑(￣Д ￣)┍',
            point: 2
        }]

        class TaskItem extends React.Component {
            handleDragStart = (e) => {
                this.props.onDragStart(this.props.id);
            }

            render() {
                let { id, title, point, username, active, onDragEnd } = this.props;
                return (
                    <div
                        onDragStart={this.handleDragStart}
                        onDragEnd={onDragEnd}
                        id={`item-${id}`}
                        className={'item' + (active ? ' active' : '')}
                        draggable="true"
                    >
                        <header className="item-header">
                            <span className="item-header-username">{username}</span>
                            <span className="item-header-point">{point}</span>
                        </header>
                        <main className="item-main">{title}</main>
                    </div>
                );
            }
        }

        // 卡片列表
        class TaskCol extends React.Component {
            state = {
                in: false
            }

            handleDragEnter = (e) => {
                // 为什么既然调用了onDragOver，html还是默认不让放入元素呢，非得改掉default行为
                e.preventDefault();
                if (this.props.canDragIn) {
                    this.setState({
                        in: true
                    })
                }
            }

            handleDragLeave = (e) => {
                e.preventDefault();
                if (this.props.canDragIn) {
                    this.setState({
                        in: false
                    })
                }
            }

            handleDrop = (e) => {
                e.preventDefault();
                this.props.dragTo(this.props.status);
                this.setState({
                    in: false
                })
            }

            render() {
                let { status, children } = this.props;
                return (
                    <div
                        id={`col-${status}`}
                        className={'col'}
                        // onDragEnter={this.handleDragEnter}
                        onDragLeave={this.handleDragLeave}
                        onDragOver={this.handleDragEnter}
                        onDrop={this.handleDrop}
                    >
                        <header className="col-header">
                            {STATUS_CODE[status]}
                        </header>
                        <main className={'col-main' + (this.state.in ? ' active' : '')}>
                            {children}
                        </main>
                    </div>
                );
            }
        }

        class App extends React.Component {
            state = {
                tasks: tasks,
                activeId: null
            }

            /**
             * 传入被拖拽任务项的 id
             */
            onDragStart = (id) => {
                this.setState({
                    activeId: id
                })
            }

            dragTo = (status) => {
                let { tasks, activeId } = this.state;
                console.log("tasks", tasks);
                console.log("activeId", activeId);
                let task = tasks[activeId];
                if (task.status !== status) {
                    task.status = status;
                    this.setState({
                        tasks: tasks
                    })
                }
                this.cancelSelect();
                console.log("tasks2", tasks);
            }

            cancelSelect = () => {
                this.setState({
                    activeId: null
                })
            }

            render() {
                let { tasks, activeId } = this.state;
                let { onDragStart, onDragEnd, cancelSelect } = this;
                return (
                    <div className="task-wrapper">
                        {
                            Object.keys(STATUS_CODE).map(status =>
                                <TaskCol
                                    status={status}
                                    key={status}
                                    dragTo={this.dragTo}
                                    // 本身框内拖动会返回fasle, 也就不会变色了
                                    canDragIn={activeId != null && tasks[activeId].status !== status}
                                    // canDragIn={true}
                                >
                                    {tasks.filter(t => t.status === status).map(t =>
                                        <TaskItem
                                            key={t.id}
                                            active={t.id === activeId}
                                            id={t.id}
                                            title={t.title}
                                            point={t.point}
                                            username={t.username}
                                            onDragStart={onDragStart}
                                            onDragEnd={cancelSelect}
                                        />)
                                    }
                                </TaskCol>
                            )
                        }
                    </div>
                )
            }
        }

        ReactDOM.render(
            <App />,
            document.getElementById('app')
        );
    </script>
</body>

</html>

<!-- 参考链接，太酷了：https://www.cnblogs.com/wenruo/p/10225377.html 
    这个例子非常酷，
    1. 核心在于对数据list tasks的渲染：因为react是自动更新dom的，只需要state就好
    所以我们只需要聚焦于改变tasks这个json，就会自动呈现出不同的效果
    而核心又是修改tasks中的status
    2. 充分利用好drag drop等属性
-->